Skip to content

Split reverse-mode thunk for the IIP Enzyme rule (alternative to #63)#64

Merged
ChrisRackauckas merged 1 commit into
SciML:mainfrom
ChrisRackauckas-Claude:enzyme-reverse-split-thunk
Jul 5, 2026
Merged

Split reverse-mode thunk for the IIP Enzyme rule (alternative to #63)#64
ChrisRackauckas merged 1 commit into
SciML:mainfrom
ChrisRackauckas-Claude:enzyme-reverse-split-thunk

Conversation

@ChrisRackauckas-Claude

Copy link
Copy Markdown
Contributor

Please ignore until reviewed by @ChrisRackauckas. Draft, opened by an automated agent. Alternative design to #63 — see the known limitation below.

The Enzyme-native version of the IIP reverse-rule fix. Instead of snapshotting arguments and re-running autodiff(Reverse, …) in the reverse pass (#63), this delegates to Enzyme's own split reverse-mode autodiff_thunk on the unwrapped function:

  • augmented_primalautodiff_thunk(ReverseSplitModified(mode, Val(overwritten(config))), Const{typeof(f)}, Const, argtypes...), runs the forward pass, stashes (tape, reverse_thunk). Forwarding overwritten as ModifiedBetween makes Enzyme's own tape cache the mutated arguments (the ODE-integrator u), so there's no manual snapshot/restore.
  • reverse runs the stashed reverse thunk; _revslot rebuilds its per-arg return exact-typed so Active args are correct (not zeroed / union-typed → ReverseRuleReturnError).

Passes the whole test/Enzyme suite, including the mutation, multi-step-integrator, and mixed Duplicated/Active regression tests.

⚠️ Known limitation — do not merge ahead of #63 yet

On the full nested EnsembleProblem adjoint (SciML/SciMLSensitivity.jl#1424), the nested autodiff_thunk inside augmented_primal re-triggers the Julia-1.11 _dispatch_ensemble_solve GC-root-rewrite segfault in Enzyme's post_optimize! — the #3175 crash that the lightweight snapshot rule in #63 avoids. Verified: identical script, AutoSpecialize ensemble adjoint → #63 = exit 0 (no segfault), this branch = SIGSEGV in optimize.jl:485.

So #63 is the mergeable fix today; this is the target design once EnzymeAD/Enzyme.jl#3175 is resolved (a SciML-free reduction of #3175 wasn't found — a self-contained split-thunk custom rule over a 100-trajectory × 120-array aggregate compiles fine; the crash needs the real _dispatch_ensemble_solve IR).

🤖 Generated with Claude Code

…ciML#63)

Reimplements the Const-return (IIP) reverse rule by delegating to Enzyme's own
split reverse-mode `autodiff_thunk` on the *unwrapped* function, rather than
snapshotting arguments and re-running `autodiff(Reverse, …)` in the reverse
pass (the approach in the snapshot PR):

- `augmented_primal` builds `autodiff_thunk(ReverseSplitModified(…, overwritten))`
  and runs its forward pass; the `ModifiedBetween == overwritten` flags make
  Enzyme's own tape cache any argument the caller mutates before the reverse
  pass. It stashes `(tape, reverse_thunk)`.
- `reverse` runs the stashed reverse thunk; `_revslot` rebuilds its per-argument
  return exactly typed (so `Active` args are correct, not zeroed / union-typed).

This is the more Enzyme-native design and passes every isolated test (mutation,
multi-step integrator, mixed Duplicated/Active). It is offered as an alternative
to SciML#63.

KNOWN LIMITATION: on the full nested `EnsembleProblem` adjoint (SciMLSensitivity
#1424) the nested `autodiff_thunk` inside `augmented_primal` re-triggers the
Julia-1.11 `_dispatch_ensemble_solve` GC-root-rewrite segfault in Enzyme's
`post_optimize!` (EnzymeAD/Enzyme.jl #3175), which the lightweight snapshot rule
in SciML#63 avoids. So this should not merge ahead of SciML#63 until #3175 is fixed
upstream; it is the target design once it is.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BZwV7CBdLPqVpJrvRAWcv6
@ChrisRackauckas ChrisRackauckas marked this pull request as ready for review July 5, 2026 15:59
@ChrisRackauckas ChrisRackauckas merged commit 07a943b into SciML:main Jul 5, 2026
15 of 16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants